Skip to content

Add RAII for PIDL types (BSTR-style) and FreeWith(CoTaskMemFree) for CoTaskMem string out-params#2276

Draft
antmor wants to merge 3 commits into
microsoft:mainfrom
antmor:user/antmor/pidl-ilfree-safehandle
Draft

Add RAII for PIDL types (BSTR-style) and FreeWith(CoTaskMemFree) for CoTaskMem string out-params#2276
antmor wants to merge 3 commits into
microsoft:mainfrom
antmor:user/antmor/pidl-ilfree-safehandle

Conversation

@antmor

@antmor antmor commented Jul 11, 2026

Copy link
Copy Markdown

Summary

Two related improvements so CsWin32 consumers get RAII/free guidance for shell PIDLs and CoTaskMem-allocated strings instead of hand-rolling cleanup.

1. PIDL RAII (autoTypes.json) - BSTR-style typedef

Model PIDLIST_RELATIVE, PIDLIST_ABSOLUTE, and PITEMID_CHILD as NativeTypedef handles following the BSTR precedent (a distinct typedef wrapping its real pointee, char*, with a CloseApi):

{ "Name": "PIDLIST_ABSOLUTE", "ValueType": "Windows.Win32.UI.Shell.Common.ITEMIDLIST*", "CloseApi": "CoTaskMemFree", "AlsoUsableFor": "PIDLIST_RELATIVE", "NativeTypedef": true }
  • Keeps the strong ITEMIDLIST* pointee (no void*), analogous to BSTR's char*.
  • CloseApi = CoTaskMemFree -> CsWin32 generates a SafeHandle.
  • AlsoUsableFor links absolute/child to relative (as HMODULE/HINSTANCE do).
  • Namespace is inherited from the CloseApi (as BSTR/HANDLE do).

2. FreeWith(CoTaskMemFree) for CoTaskMem string out-params (emitter.settings.rsp)

The unique_cotaskmem_string cases - PWSTR/LPOLESTR out-params that must be freed with CoTaskMemFree, mirroring the existing SHGetKnownFolderPath::ppszPath precedent:

  • SHGetNameFromIDList::ppszName
  • StringFromCLSID::lplpsz
  • StringFromIID::lplpsz
  • IShellItem::GetDisplayName::ppszName

Open questions / please validate in CI

  1. Struct-pointer ValueType (ITEMIDLIST*) has no existing precedent (all current entries use primitives/void*/IntPtr). Confirm the generator/emitter accepts a fully-qualified struct-pointer payload.
  2. CoTaskMemFree namespace: if it resolves to multiple namespaces the same-namespace check throws; also inheriting System.Com relocates the PIDL typedefs from UI.Shell.Common - the winmd baseline diff will show this. If undesirable, alternatives are an explicit Namespace or using ILFree (fewer partitions).

Testing

Local DoAll.ps1 scrape is blocked in my environment by a VS 2026 / MSVC 14.51 vs pinned libclang 17 mismatch (__builtin_verbose_trap), unrelated to this change. Relying on CI (pr-validation) for the winmd/API diff.

antmor and others added 3 commits July 10, 2026 21:44
…feHandle

Model PIDLIST_RELATIVE, PIDLIST_ABSOLUTE, and PITEMID_CHILD as NativeTypedef
handles with CloseApi=ILFree in autoTypes.json, following the HLOCAL/HGLOBAL
(void* + free-function) precedent. This lets CsWin32 emit an ILFree-based
SafeHandle for PIDLs instead of requiring consumers to hand-roll RAII around
SHGetIDListFromObject, ILCreateFromPath, SHGetKnownFolderIDList, etc.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…or string out-params

- PIDL typedefs (PIDLIST_RELATIVE/ABSOLUTE, PITEMID_CHILD) now follow the BSTR
  pattern: NativeTypedef wrapping the real ITEMIDLIST* pointee (no void*) with
  CloseApi=CoTaskMemFree, so CsWin32 generates a strongly-typed SafeHandle.
  Namespace is inherited from the CloseApi (as BSTR/HANDLE do).
- Add FreeWith(CoTaskMemFree) annotations for PWSTR/LPOLESTR out-params that must
  be freed with CoTaskMemFree (the unique_cotaskmem_string cases): SHGetNameFromIDList,
  StringFromCLSID, StringFromIID, IShellItem::GetDisplayName. Mirrors the existing
  SHGetKnownFolderPath::ppszPath precedent.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Placing the PIDL NativeTypedefs in the CloseApi's inherited namespace put them in
System.Com while their ITEMIDLIST* payload lives in Shell.Common, creating a
System.Com <-> Shell.Common cycle (NoCyclicalNamespaces failure).

Fix keeps the strong ITEMIDLIST* typing by co-locating everything in Shell.Common:
- PIDL typedefs (PIDLIST_RELATIVE/ABSOLUTE, PITEMID_CHILD) explicitly in
  Windows.Win32.UI.Shell.Common with ValueType=ITEMIDLIST* and CloseApi=ILFree.
- Move ILFree into Shell.Common via requiredNamespacesForNames.rsp so the CloseApi
  is intra-namespace (satisfies the same-namespace rule, adds no outward edge).
- Restore InvalidHandleValues=[0] (required by RaiiFreeAttributeTests).

Validated locally against MSVC 14.44: all tests pass (MetadataUtils 17/17,
Win32MetadataScraper 44/44, Windows.Win32.Tests 13/13).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@jevansaks

Copy link
Copy Markdown
Member

Thanks for the contribution @antmor! I see this is still a draft, are you still working on this? Did you want feedback on it?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants